/*
 * FunctionObject.java - ScriptME
 * 
 * Copyright (c) 2009 Cesar Henriques <cesar at alttab.com.ar>.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Based on FESI Project
 * 
 * Contributors:
 * 	Jean-Marc Lugrin - initial API and implementation
 * 	Cesar Henriques <cesar at alttab.com.ar> - J2ME Porting and Extensions
 */
package org.scriptme.ecmascript.data;

import java.util.Vector;

import org.scriptme.ecmascript.ast.ASTFormalParameterList;
import org.scriptme.ecmascript.ast.ASTStatementList;
import org.scriptme.ecmascript.ast.EcmaScriptTreeConstants;
import org.scriptme.ecmascript.exceptions.EcmaScriptException;
import org.scriptme.ecmascript.exceptions.EcmaScriptParseException;
import org.scriptme.ecmascript.interpreter.EcmaScriptVariableVisitor;
import org.scriptme.ecmascript.interpreter.Evaluator;
import org.scriptme.ecmascript.interpreter.FunctionEvaluationSource;
import org.scriptme.ecmascript.interpreter.StringEvaluationSource;
import org.scriptme.ecmascript.parser.EcmaScript;
import org.scriptme.ecmascript.parser.ParseException;
import org.scriptme.util.StringReader;

/**
 * Implements the EcmaScript Function singleton.
 */
public class FunctionObject extends BuiltinFunctionObject implements
		EcmaScriptTreeConstants {

	/** The debug parse. */
	static boolean debugParse = false;

	/**
	 * Instantiates a new function object.
	 * 
	 * @param prototype
	 *            the prototype
	 * @param evaluator
	 *            the evaluator
	 */
	FunctionObject(ESObject prototype, Evaluator evaluator) {
		super(prototype, evaluator, "Function", 1);
	}

	// overrides
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.data.FunctionPrototype#getESClassName()
	 */
	public String getESClassName() {
		return "Function";
	}

	// overrides - call and new have the same effect
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.data.FunctionPrototype#callFunction(org.scriptme.ecmascript.data.ESObject,
	 *      org.scriptme.ecmascript.data.ESValue[])
	 */
	public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
			throws EcmaScriptException {
		return doConstruct(thisObject, arguments);
	}

	// overrides - build a new function
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.data.ESObject#doConstruct(org.scriptme.ecmascript.data.ESObject,
	 *      org.scriptme.ecmascript.data.ESValue[])
	 */
	public ESObject doConstruct(ESObject thisObject, ESValue[] arguments)
			throws EcmaScriptException {
		ESObject fp = evaluator.getFunctionPrototype();
		ConstructedFunctionObject theFunction = null;
		ASTFormalParameterList fpl = null;
		ASTStatementList sl = null;

		StringBuffer parameters = new StringBuffer();
		int nArgs = arguments.length;
		int i;
		for (i = 0; i < (nArgs - 1); i++) {
			if (i > 0) {
				parameters.append(",");
			}
			String arg = arguments[i].toString();
			parameters.append(arg);
		}
		String body = arguments[i].toString();

		// System.out.println("P:'"+parameters+"'");
		// System.out.println("B:'"+body+"'");

		String trimedParams = parameters.toString().trim();

		String fullFunctionText = "function anonymous (" + trimedParams + ") {"
				+ body.toString() + "}";

		StringReader is;
		EcmaScript parser;

		// Special case for empty parameters
		if (trimedParams.length() == 0) {
			fpl = new ASTFormalParameterList(JJTFORMALPARAMETERLIST);
		} else {
			is = new StringReader(trimedParams);
			parser = new EcmaScript(is);
			try {
				fpl = (ASTFormalParameterList) parser.FormalParameterList();
				is.close();
			} catch (ParseException e) {
				if (debugParse) {
					System.out
							.println("[[PARSING ERROR DETECTED: (debugParse true)]]");
					System.out.println(e.getMessage());
					System.out.println("[[BY ROUTINE:]]");
					e.printStackTrace();
					System.out.println();
				}
				throw new EcmaScriptParseException(e,
						new StringEvaluationSource(fullFunctionText, null));
			}
		}
		is = new StringReader(body.toString());
		parser = new EcmaScript(is);
		try {
			sl = (ASTStatementList) parser.StatementList();
			is.close();
		} catch (ParseException e) {
			if (debugParse) {
				System.out
						.println("[[PARSING ERROR DETECTED: (debugParse true)]]");
				System.out.println(e.getMessage());
				System.out.println("[[BY ROUTINE:]]");
				e.printStackTrace();
				System.out.println();
			}
			throw new EcmaScriptParseException(e, new StringEvaluationSource(
					fullFunctionText, null));
		}

		FunctionEvaluationSource fes = new FunctionEvaluationSource(
				new StringEvaluationSource(fullFunctionText, null),
				"<anonymous function>");
		EcmaScriptVariableVisitor varDeclarationVisitor = evaluator
				.getVarDeclarationVisitor();
		Vector variableNames = varDeclarationVisitor
				.processVariableDeclarations(sl, fes);

		theFunction = ConstructedFunctionObject.makeNewConstructedFunction(
				evaluator, "anonymous", fes, fullFunctionText, fpl
						.getArguments(), variableNames, sl);

		return theFunction;
	}

	// overrides
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.data.BuiltinFunctionObject#toString()
	 */
	public String toString() {
		return "<Function>";
	}
}
